//
// Created on 2025/11/10.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef PRE_EXPORT_EXAMPLE_PRE_EXPORT_H
#define PRE_EXPORT_EXAMPLE_PRE_EXPORT_H
#include <memory>
#include <shared_mutex>
#include <fstream>
#include <sstream>
#include <rawfile/raw_file.h>
#include <rawfile/raw_file_manager.h>
#include "pre_export/background_cache.h"
#include "pre_export/codec_common.h"
#include <cassert>
#include <thread>

#define CHECK_RETURN_ERROR_IF_FALSE(expr)       \
    do {                                        \
        if (!(expr)) {                          \
            PRE_EXPORT_LOGE("check failed");    \
            return ResultCode::ERROR;           \
        }                                       \
    } while(false)                              \

class ExampleExportTest {
public:
    ExampleExportTest(NativeResourceManager *m) {
        mgr_ = m;
    }
    
    std::string testAllCase() {
        std::stringstream ss;
        ss << "example start\n";
        int index = 0;
        auto example = ExampleExportTest(mgr_);
        auto result = example.example();
        if (result != ResultCode::OK) {
                ss << "\nExample" << index << " failed: " << static_cast<int>(result);
            } else {
                ss << "\nExample" << index << " pass: " << static_cast<int>(result);
        }
        return ss.str();
    }
    
    ResultCode example() {
        PRE_EXPORT_LOGI("example %{public}s start", __func__);
        // 1. 预导入素材hashX
        // c1 [0-9] size 10
        uint64_t hash1Start = 0;
        uint64_t hash1End = 9;
        std::shared_ptr<BackgroundCache> c1 = std::make_shared<BackgroundCache>(
            hash1End + 1, codecWidth, codecHeight, codecFrameRate, codecPixelFormat);
        auto code = c1->SetCachePath(cachePath_);
        CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
        code = c1->SetHash("hashX");
        CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
        // 2. 触发hashX的预导出, 并检查缺失帧
        uint64_t frameStart = hash1Start;
        uint64_t frameEnd = hash1End;
        bool b = c1->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::PRE_EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == false);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == hash1Start);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == hash1End);
        // 3. 喂缺失帧
        putFrameInRange(c1, frameStart, frameEnd, ExportType::PRE_EXPORT);
        // 4. 再次检查至补充完帧
        frameStart = hash1Start;
        frameEnd = hash1End;
        b = c1->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::PRE_EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == true);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == hash1Start);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == hash1End);
        // 5. 通知中断预导出
        c1->NotifyInterrupt();
        CHECK_RETURN_ERROR_IF_FALSE(c1->IsInterrupt() == true);
        // 6. 再次检查以恢复中断
        frameStart = hash1Start;
        frameEnd = hash1End;
        b = c1->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::PRE_EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == true);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == hash1Start);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == hash1End);
        
        // 7. 添加特效hashY
        // hashY[0-4] 映射hashX[3, 7] size 5 偏移量 3
        uint64_t hash2Start = 0;
        uint64_t hash2End = 4;
        uint64_t offset = 3;
        std::shared_ptr<BackgroundCache> c2 = std::make_shared<BackgroundCache>(
            hash2End + 1, codecWidth, codecHeight, codecFrameRate, codecPixelFormat);
        code = c2->SetCachePath(cachePath_);
        CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
        code = c2->SetHash("hashY");
        CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
    
        // 8.导出时: 先检查是否需要帧
        /* 关系图: 每3(frameRate)帧编码为一组
           导出范围: c1[0-2] c2[3-7] c1[8-9]
        c1[0-9]  ==> 0[I帧]  1  2    3[I帧]  4  5    6[I帧]  7   8    9[I帧]
        c2[3-7]  ==>                 0[I帧]  1  2    3[I帧]  4
        导出：        |----------|    |-----------------------|  |--------|
        结果：         [0 - 2] ---> c1的刚好3帧，完整可用
                                     [3-7]  c2的缓存，完整可用
                                                                 8 c1的缓存段[6-8]被切割了，需单独重新缓存8
                                                                        9 单独的I帧，完整可用        
        */
        // c1[0-2]
        frameStart = 0;
        frameEnd = 2;
        b = c1->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == true);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == 0);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == 2);
        // c2[3-7]
        // 触发hashY的预导出, 并检查缺失帧
        frameStart = hash2Start;
        frameEnd = hash2End;
        b = c2->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == false);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == hash2Start);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == hash2End);
        // 提供全部帧
        putFrameInRange(c2, frameStart, frameEnd, ExportType::EXPORT, offset);
        // 再次检查至补充完帧
        frameStart = hash2Start;
        frameEnd = hash2End;
        b = c2->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == true);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == hash2Start);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == hash2End);
        // c1[8-9]  示例需要补充8
        frameStart = 8;
        frameEnd = 9;
        b = c1->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == false);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == 8);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == 8);
        putFrameInRange(c1, frameStart, frameEnd, ExportType::EXPORT);
        // c1[8-9] 再次检查至补充完帧
        frameStart = 8;
        frameEnd = 9;
        b = c1->CheckFrameRange(frameStart, &frameStart, &frameEnd, ExportType::EXPORT);
        CHECK_RETURN_ERROR_IF_FALSE(b == true);
        CHECK_RETURN_ERROR_IF_FALSE(frameStart == 8);
        CHECK_RETURN_ERROR_IF_FALSE(frameEnd == 9);
        
        // 9. 导出, 示例保存到文件
        // c1[0-2]
        code = c1->RunUntilEncodeFinished(timeout);
        CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
        std::unique_ptr<std::ofstream> fd = std::make_unique<std::ofstream>();
        std::string outputFile = cachePath_ + "testFinal.h264";
        fd->open(outputFile, std::ios::out | std::ios::binary | std::ios::ate);
        for(uint64_t i = 0; i <= 2; i++) {
            void *outFrameData = nullptr;
            uint32_t outFrameSize = 0;
            code = c1->GetFrame(i, &outFrameData, &outFrameSize, ExportType::EXPORT);
            CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
            fd->write(reinterpret_cast<char *>(outFrameData), outFrameSize);
            free(outFrameData);
        }
        // c2[3-7]
        code = c2->RunUntilEncodeFinished(timeout);
        CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
        for(uint64_t i = 0; i <= 4; i++) {
            void *outFrameData = nullptr;
            uint32_t outFrameSize = 0;
            code = c2->GetFrame(i, &outFrameData, &outFrameSize, ExportType::EXPORT);
            CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
            fd->write(reinterpret_cast<char *>(outFrameData), outFrameSize);
            free(outFrameData);
        }
        // C1[8-9]
        code = c1->RunUntilEncodeFinished(timeout);
        for(uint64_t i = offset + hash2End + 1; i <= hash1End; i++) {
            void *outFrameData = nullptr;
            uint32_t outFrameSize = 0;
            code = c1->GetFrame(i, &outFrameData, &outFrameSize, ExportType::EXPORT);
            CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
            fd->write(reinterpret_cast<char *>(outFrameData), outFrameSize);
            free(outFrameData);
        }       
        fd->close();
        return ResultCode::OK;
    }
    
    ResultCode readRawFile(std::string path, void **data, uint64_t *size) {
        CHECK_RETURN_ERROR_IF_FALSE(mgr_ != nullptr);
        RawFile *descriptor = OH_ResourceManager_OpenRawFile(mgr_, path.c_str());
        CHECK_RETURN_ERROR_IF_FALSE(descriptor != nullptr);
        long bufSize = OH_ResourceManager_GetRawFileSize(descriptor);
        char *buf = (char *)malloc(bufSize);
        if (buf == NULL) {
            OH_ResourceManager_CloseRawFile(descriptor);
            return ResultCode::SYSTEM_ERROR;
        }
        long readSize = OH_ResourceManager_ReadRawFile(descriptor, buf, bufSize);
        if (readSize < 1) {
            free(buf);
            OH_ResourceManager_CloseRawFile(descriptor);
            return ResultCode::SYSTEM_ERROR;
        }
        *data = buf;
        *size = bufSize;
        OH_ResourceManager_CloseRawFile(descriptor);
        return ResultCode::OK;
    }
    
    ResultCode putFrameInRange(std::shared_ptr<BackgroundCache> c, uint64_t frameStart, uint64_t frameEnd,
                               ExportType exportType, uint64_t offset = 0)
    {
        for(uint64_t i = frameStart; i <= frameEnd; i++) {
            void *inFrameData = nullptr;
            uint64_t size = 0;
            auto code = readRawFile("files" + std::to_string(i + offset) + ".yuv",  &inFrameData, &size);
            CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
            CHECK_RETURN_ERROR_IF_FALSE(inFrameData != nullptr);
            CHECK_RETURN_ERROR_IF_FALSE(size == frameWidth * frameHeight);
            code = c->PutFrame(i, inFrameData, frameWidth, frameHeight, frameWidth, exportType);
            CHECK_RETURN_ERROR_IF_FALSE(code == ResultCode::OK);
            if (inFrameData != nullptr) {
                free(inFrameData);
                inFrameData = nullptr;
            }
        }
        return ResultCode::OK;
    }
    
private:
    NativeResourceManager *mgr_;
    uint32_t frameWidth = 3120;
    uint32_t frameHeight = 3136;
    uint32_t codecWidth = 3120;
    uint32_t codecHeight = 2080; 
    // uint32_t codecFrameRate = 60;
    uint32_t codecFrameRate = 3;
    OH_AVPixelFormat codecPixelFormat = AV_PIXEL_FORMAT_NV12;
    // /data/app/el2/100/base/com.example.pre_export/temp/
    std::string cachePath_ = "/data/storage/el2/base/temp/";
    std::uint64_t timeout = 10;
};

#endif //PRE_EXPORT_EXAMPLE_PRE_EXPORT_H
